package org.pc2r.game.prepare;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;

import org.pc2r.game.Game;
import org.pc2r.game.GameManager;
import org.pc2r.game.model.Player;
import org.pc2r.protocole.EnumRequest;
import org.pc2r.protocole.message.EmptyMessage;

public class Preparator implements Callable<Boolean> {

  private GameManager manager;
  private Game game;
  private Logger logger;

  public Preparator(GameManager manager, Logger logger) {
    this.manager = manager;
    this.logger = logger;
    this.game = manager.getGame();
  }

  @Override
  public Boolean call() {
    // On prend tous les joueurs
    List<String> players = game.players();
    int size = players.size();
    // On lance 4 threads dans un pool fixe pour paralléliser
    // les récupérations de positionnement.
    // Les 4 joueurs placent en même temps leurs sous marins
    ExecutorService pool = Executors.newFixedThreadPool(size);
    List<Future<Player>> futures = new ArrayList<>(size);
    // Chaque joueur va s'ajouter dans la liste. Ainsi le premier à s'ajouter
    // sera le premier à jouer etc...
    List<Player> sortedList = new ArrayList<>(size);
    try {
      for (String player : players) {
        // On lance la tâche
        Future<Player> submit = pool.submit(new Positionner(logger, game, player, sortedList));
        futures.add(submit);
      }
    } finally {
      // On kill pour libérer les ressources
      pool.shutdown();
    }
    int i = 0;
    for (String player : players) {
      try {
        // On laisse 5minutes à chaque joueur pour effectuer ses placements,
        // sinon on le vire
        futures.get(i).get(5L, TimeUnit.MINUTES);
        // On ajoute les nouveaux sous-marins.
        // On envoie le ALLYOURBASE au joueur
        game.send(player, new EmptyMessage(EnumRequest.ALLYOURBASE));
        ++i;
      } catch (InterruptedException | ExecutionException | TimeoutException | IOException exception) {
        // Le joueur spécifié a fait des bêtises, on le vire
        if (manager.removeAndStop(player)) {
          return false;
        }
      }
    }
    // On a la liste des joueurs triées
    game.addAllPlayers(sortedList);
    return true;
  }

}
